package me.chyxion.spring.ext;

import java.io.File;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor;

import me.chyxion.spring.ext.view.DefaultViewResolver.JSONView;

/**
 * @version 0.0.3
 * @since 0.0.1
 * @author Shaun Chyxion <br />
 * chyxion@163.com <br />
 * Jul 16, 2014 10:03:15 PM
 */
public class BaseReturnValueHandler implements HandlerMethodReturnValueHandler {
	private static final Logger log = LoggerFactory.getLogger(BaseReturnValueHandler.class);

	private HttpEntityMethodProcessor resourceProcessor = 
			new HttpEntityMethodProcessor(Arrays.<HttpMessageConverter<?>>asList(
					new ResourceHttpMessageConverter()));
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return true;
    }

    @Override
    public void handleReturnValue(Object returnValue,
            MethodParameter returnType, 
            ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest) throws Exception {
    	log.info("Return Value Handler, Handle Return Value [{}], Return Type [{}].", 
    			returnValue, returnType);
    	if (!handleResource(returnValue, 
    			returnType, mavContainer, webRequest)) {
    		DataModel model = null;
    		if (returnValue instanceof DataModel) {
    			model = (DataModel) returnValue;
    		}
    		else {
    			model = new DataModel(returnValue);
    		}
    		mavContainer.setView(new JSONView(model));
    	}
    }

    /**
     * handle resource
     * @param returnValue
     * @param returnType
     * @param mavContainer
     * @param webRequest
     * @return
     * @throws Exception
     */
    private boolean handleResource(Object returnValue,
            MethodParameter returnType, 
            ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest) throws Exception {
    	ResourceModel resourceModel = null;
    	if (returnValue instanceof File) {
        	log.info("Found File Return.");
        	resourceModel = new ResourceModel((File) returnValue);
        }
        else if (returnValue instanceof InputStream) {
        	log.info("Found Input Stream Return.");
        	resourceModel = new ResourceModel((InputStream) returnValue);
        }
        else if (returnValue instanceof Resource) {
        	log.info("Found Resouce Return.");
        	resourceModel = new ResourceModel((Resource) returnValue);
        }
        else if (returnValue instanceof byte[]) {
        	log.info("Found Byte Array Return.");
        	resourceModel = new ResourceModel((byte[]) returnValue);
        }
        else if (returnValue instanceof ResourceModel) {
        	log.info("Found Download Model Return.");
        	resourceModel = (ResourceModel) returnValue;
        }
    	boolean bRtn = false;
        // resource model
        if (resourceModel != null) {
        	HttpHeaders headers = new HttpHeaders();
        	String name = resourceModel.getName();
        	if (StringUtils.isNotBlank(name)) {
        		log.info("Found Resource Name [{}], Set Content-Disposition Header.", name);
        		name = URLEncoder.encode(name, CharEncoding.UTF_8);
        		StringBuilder sbDispo = 
        			new StringBuilder("attachment; filename=\"")
        			.append(name)
        			.append("\"; filename*=utf-8''")
        			.append(name);
        		headers.set(HttpHeaders.CONTENT_DISPOSITION, 
        			sbDispo.toString());
        	}
        	headers.setContentType(
        		MediaType.valueOf(resourceModel.getContentType()));
        	resourceProcessor.handleReturnValue(
        		new ResponseEntity<Resource>(resourceModel.getResource(), 
        		headers, 
        		HttpStatus.OK), returnType, mavContainer, webRequest);
        	bRtn = true;
        }
    	return bRtn;
    }
}
